﻿/********
* 
* 
*  Description:
*  Create By Kain at 4/2/2011 2:30:46 PM
*  
*
*  Revision History:
*  Date                  Who                 What
*  
* 
*/

#region

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Security.Policy;
using System.Reflection;
using System.Security;
using System.Collections;
using System.Diagnostics;

#endregion

namespace Cherry.Agile.ServiceModel.Runtime
{
    public class DomainManager
    {
        private static string shadowCopyPath;
        public static string ShadowCopyPath
        {
            get
            {
                if (shadowCopyPath == null)
                { 
                    shadowCopyPath = Path.Combine(Path.GetTempPath(), @"nunit20\ShadowCopyCache");
                }

                return shadowCopyPath;
            }
        }

        public static AppDomain CreateDomain(ServicePackage package)
        {
            AppDomainSetup setup = new AppDomainSetup();

            //For paralell tests, we need to use distinct application name
            setup.ApplicationName = "Service" + "_" + Environment.TickCount;

            FileInfo serviceFile = new FileInfo(package.FullName);

            string appBase = package.BasePath;
            string configFile = package.ConfigurationFile;
            string binPath = package.PrivateBinPath;


            if (string.IsNullOrEmpty(appBase))
                appBase = serviceFile.DirectoryName;

            if (string.IsNullOrEmpty(configFile))
            {
                configFile = serviceFile.Name + ".config";
                if (!File.Exists(configFile))
                {
                    configFile  = Path.Combine(serviceFile.DirectoryName, "app.config");
                    if (!File.Exists(configFile))
                    {
                        throw new ArgumentNullException("没有找到对应服务的配置文件：" + package.FullName);
                    }
                }
            }

            setup.ApplicationBase = appBase;
            
            setup.ConfigurationFile = appBase != null && configFile != null
                                        ? Path.Combine(appBase, configFile)
                                        : configFile;
            binPath = appBase;

            setup.PrivateBinPath = binPath;

            if (package.GetSetting("ShadowCopyFiles", true))
            {
                setup.ShadowCopyFiles = "true";
                setup.ShadowCopyDirectories = appBase;
                setup.CachePath = GetCachePath();
            }
            else
                setup.ShadowCopyFiles = "false";

            string domainName = "service-domain-" + package.Name;
            // Setup the Evidence
            Evidence evidence = new Evidence(AppDomain.CurrentDomain.Evidence);
            if (evidence.GetAssemblyEnumerator().Current == null)
            {
                Zone zone = new Zone(SecurityZone.MyComputer);
                evidence.AddHostEvidence<Zone>(zone);
                Assembly assembly = Assembly.GetExecutingAssembly();
                Url url = new Url(assembly.CodeBase);
                evidence.AddHostEvidence<Url>(url);
                Hash hash = new Hash(assembly);
                evidence.AddHostEvidence<Hash>(hash);
            }

            return AppDomain.CreateDomain(domainName, evidence, setup);
 
        }

        static private string GetCachePath()
        {
            int processId = Process.GetCurrentProcess().Id;
            long ticks = DateTime.Now.Ticks;
            string cachePath = Path.Combine(ShadowCopyPath, processId.ToString() + "_" + ticks.ToString());

            try
            {
                DirectoryInfo dir = new DirectoryInfo(cachePath);
                if (dir.Exists) dir.Delete(true);
            }
            catch (Exception ex)
            {
                throw new ApplicationException(
                    string.Format("Invalid cache path: {0}", cachePath),
                    ex);
            }

            return cachePath;
        }
        
        public static string GetCommonAppBase(IList assemblies)
        {
            string commonBase = null;

            foreach (string assembly in assemblies)
            {
                string dir = Path.GetFullPath(Path.GetDirectoryName(assembly));
                if (commonBase == null)
                    commonBase = dir;
                else while (!PathUtils.SamePathOrUnder(commonBase, dir) && commonBase != null)
                        commonBase = Path.GetDirectoryName(commonBase);
            }

            return commonBase;
        }

        public static string GetPrivateBinPath(string basePath, IList assemblies)
        {
            StringBuilder sb = new StringBuilder(200);
            ArrayList dirList = new ArrayList();

            foreach (string assembly in assemblies)
            {
                string dir = PathUtils.RelativePath(
                    Path.GetFullPath(basePath),
                    Path.GetDirectoryName(Path.GetFullPath(assembly)));
                if (dir != null && dir != "." && !dirList.Contains(dir))
                {
                    dirList.Add(dir);
                    if (sb.Length > 0)
                        sb.Append(Path.PathSeparator);
                    sb.Append(dir);
                }
            }

            return sb.Length == 0 ? null : sb.ToString();
        }

    }
}
